#include "heaptimer.h"

void HeapTimer::Add(int id, int timeOut, const TimeoutCallBack &cb)
{
    assert(id >= 0);
    size_t i;
    if(ref_.count(id) == 0) {
        i = timerHeap_.size();
        ref_[id] = i;
        timerHeap_.push_back({id, Clock::now() + MS(timeOut), cb});
        SiftUp(i);
    } else {
        i = ref_[id];
        timerHeap_[i].cb = cb;
        timerHeap_[i].expires = Clock::now() + MS(timeOut);
        if(!SiftDown(i, timerHeap_.size())) {
            SiftUp(i);
        }
    }
}

void HeapTimer::DoWork(int id)
{
    if(timerHeap_.empty() || ref_.count(id) == 0) {
        return;
    }
    size_t i = ref_[id];
    TimerNode timerNode = timerHeap_[i];
    timerNode.cb();
    Delete(i);
}

void HeapTimer::Delete(int index)
{
    assert(index >= 0 && index < timerHeap_.size());
    size_t i = index;
    size_t n = timerHeap_.size();
    if(i < n) {
        SwapNode(i, n);
        if(!SiftDown(i, n)) {
            SiftUp(i);
        }
    }
    ref_.erase(timerHeap_.back().id);
    timerHeap_.pop_back();
}

void HeapTimer::Adjust(int id, int timeOut)
{
    timerHeap_[ref_[id]].expires = Clock::now() + MS(timeOut);
    SiftDown(ref_[id], timerHeap_.size());
}

void HeapTimer::Pop()
{
    Delete(0);
}

void HeapTimer::Clear()
{
    timerHeap_.clear();
    ref_.clear();
}

void HeapTimer::Tick()
{
    if(timerHeap_.empty()) {
        return;
    }
    while(!timerHeap_.empty()) {
        TimerNode timerNode = timerHeap_.front();
        if(std::chrono::duration_cast<MS>(timerNode.expires - Clock::now()).count() > 0) {
            break;
        }
        timerNode.cb();
        Pop();
    }
}

int HeapTimer::GetNextTick()
{
    Tick();
    size_t res = -1;
    if(!timerHeap_.empty()) {
        res = std::chrono::duration_cast<MS>(timerHeap_.front().expires - Clock::now()).count();
        if(res < 0) {res = 0;}
    }
    return res;
}

void HeapTimer::SiftUp(size_t i)
{
    assert(i >= 0 && i < timerHeap_.size());
    size_t j = (i-1)/2;
    while (j >= 0)
    {
        if(timerHeap_[j] < timerHeap_[i]) {
            break;
        }
        SwapNode(i, j);
        i = j;
        j = (i-1)/2;
    }
    
}

bool HeapTimer::SiftDown(size_t i, size_t n)
{
    assert(i >= 0 && i < timerHeap_.size());
    assert(n >= 0 && n <= timerHeap_.size());
    int index = i;
    int j = i * 2 + 1;
    while(j < n) {
        if(j+1 < n && timerHeap_[j+1] < timerHeap_[j]) j++;
        if(timerHeap_[i] < timerHeap_[j]) {
            break;
        }
        SwapNode(i, j);
        i = j;
        j = j*2+1;
    }
    return i > index;
}

void HeapTimer::SwapNode(size_t i, size_t j)
{
    assert(i >= 0 && i < timerHeap_.size());
    assert(j >= 0 && j < timerHeap_.size());
    std::swap(timerHeap_[i], timerHeap_[j]);
    ref_[timerHeap_[i].id] = i;
    ref_[timerHeap_[j].id] = j;
}
